package com.glsc.ngateway.opmanage.service.report;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.glsc.ngateway.common.base.domain.mysql.opmanage.report.regulatory.RegRptCheckRulesDefine;
import com.glsc.ngateway.common.base.domain.mysql.opmanage.report.regulatory.RegRptDefine;
import com.glsc.ngateway.common.base.dto.opmanage.report.BiExportDto;
import com.glsc.ngateway.common.base.dto.opmanage.report.DerivativeException;
import com.glsc.ngateway.common.base.dto.opmanage.report.ExcelUploadDto;
import com.glsc.ngateway.common.base.dto.opmanage.report.RegRptInfoDto;
import com.glsc.ngateway.common.base.repo.mysql.opmanage.report.regulatory.RegRptCheckRulesDefineRepo;
import com.glsc.ngateway.common.base.repo.mysql.opmanage.report.regulatory.RegRptCheckRulesResultRepo;
import com.glsc.ngateway.common.base.repo.mysql.opmanage.report.regulatory.RegRptDefineRepo;
import com.glsc.ngateway.common.base.repo.mysql.opmanage.report.regulatory.RegRptMainRepo;
import com.glsc.ngateway.common.base.utils.ReportExportFileTypeEnum;
import com.glsc.ngateway.opmanage.service.smartbi.BiUserManager;
import org.apache.commons.lang.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.CallableStatementCallback;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigInteger;
import java.sql.CallableStatement;
import java.text.MessageFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: qiyr
 * @date: 2022/6/30 11:14
 * @description: 监管报表管理服务
 */
@Service
@Transactional(transactionManager = "transactionManagerMysql", rollbackFor = Exception.class)
public class RegulatoryReportService {

    private static final Logger logger = LoggerFactory.getLogger(RegulatoryReportService.class);

    /**
     * 合并后的维度字段
     */
    private static final String MERGE_DIMENSION = "国联证券";
    private static final String MERGE_YES = "已合并";
    private static final String MERGE_NO = "未合并";
    private static final String CHECK_PASS = "通过";
    private static final String CHECK_FAIL = "未通过";
    private static final String CHECK_NO = "未校验";
    private static final String CHECK_NO_NEED = "无需校验";
    /**
     * 场外交易监控BI报表参数
     */
    private static final String BI_PARAM_REPORT_DATE = "监测平台报告报送日期";
    private static final String BI_PARAM_DIMENSION1 = "监测平台报表维度1";
    private static final String BI_REPORT_NAME = "{0}-{1}-{2}";

    @Autowired
    private RegRptDefineRepo regRptDefineRepo;
    @Autowired
    private RegRptMainRepo regRptMainRepo;
    @Qualifier("mysqlglscdataJdbcTemplate")
    @Autowired
    private JdbcTemplate jdbcTemplateMysql;
    @Autowired
    private RegRptCheckRulesResultRepo regRptCheckRulesResultRepo;
    @Autowired
    private RegRptCheckRulesDefineRepo regRptCheckRulesDefineRepo;
    @Autowired
    private BiUserManager biUserManager;

    public List<RegRptInfoDto> info(String catid, String bizdate) {
        List<RegRptDefine> importConfigList = regRptDefineRepo.findByCatId(catid);
        defineFilter(importConfigList, bizdate);
        Set<BigInteger> reportDefineIdSet = regRptCheckRulesDefineRepo.findAllExist().stream().
                map(RegRptCheckRulesDefine::getReportDefineId).collect(Collectors.toSet());
        List<BigInteger> defineIdList = new ArrayList<>();
        Map<BigInteger, RegRptInfoDto> defineIdMap = new HashMap<>(defineIdList.size());
        importConfigList.forEach(item -> {
            BigInteger id = item.getId();
            defineIdList.add(id);
            defineIdMap.put(id, new RegRptInfoDto(item.getId(), item.getReportName(), bizdate,
                    MERGE_DIMENSION, MERGE_NO, new ArrayList<>(), null, null, CHECK_NO, item.getBiReportId(), item.getBiVersion()));
        });

        List<JSONObject> infoList = regRptMainRepo.queryInfo(defineIdList, bizdate);
        for (JSONObject jsonObject : infoList) {
            BigInteger id = jsonObject.getBigInteger("id");
            BigInteger repDefineId = jsonObject.getBigInteger("rpt_define_id");
            String dimension1 = jsonObject.getString("dimension1");
            Integer checkStatus = jsonObject.getInteger("check_status");
            String operator = jsonObject.getString("operator");
            LocalDateTime createTime = jsonObject.getTimestamp("create_time").toLocalDateTime();

            RegRptInfoDto dto = defineIdMap.get(repDefineId);
            // 若维度为“国联证券”，则说明该条记录为合并记录
            if (MERGE_DIMENSION.equals(dimension1)) {
                dto.setMergeStatus(MERGE_YES);
                dto.setOperator(operator);
                dto.setCreateTime(createTime);
                dto.setCheckStatus(checkStatus == null ? CHECK_NO : Objects.equals(checkStatus, 0) ? CHECK_PASS : CHECK_FAIL);
            } else {
                dto.getChildren().add(new RegRptInfoDto.RegRptDto(id, dimension1, bizdate, operator, createTime, dto.getBiReportId(), dto.getBiVersion()));
            }
        }
        return defineIdMap.values().stream().peek(item -> {
            if (CollectionUtils.isEmpty(item.getChildren())) {
                item.setChildren(null);
            }
            if (!reportDefineIdSet.contains(item.getId())) {
                item.setCheckStatus(CHECK_NO_NEED);
            }
        }).sorted(Comparator.comparing(RegRptInfoDto::getId)).collect(Collectors.toList());
    }

    public List<List> merge(List<String> idList, String bizdate, String username) {
        List<List> ret = new ArrayList<>();
        try {
            for (String id : idList) {
                List result = (List) jdbcTemplateMysql.execute(
                        con -> {
                            String storedProc = "{CALL glsc_data.p_reg_rpt_merge" + "(?,?,?,?,?)}";// 调用的sql
                            CallableStatement cs = con.prepareCall(storedProc);
                            cs.setString(1, id);
                            cs.setString(2, bizdate);
                            cs.setString(3, username);
                            cs.registerOutParameter(4, 4);//设置输出参数 —— 必须在执行存储过程之前进行注册
                            cs.registerOutParameter(5, 12);
                            return cs;
                        }, (CallableStatementCallback) cs -> {
                            List<String> res = new ArrayList<>();
                            cs.execute();
                            Integer a = cs.getInt(4);
                            String s = String.valueOf(a);
                            res.add(s);
                            String ss = cs.getString(5);
                            res.add(ss);
                            return res;
                        });
                ret.add(result);
            }
        } catch (Exception e) {
            throw DerivativeException.error(e.getMessage());
        }

        return ret;
    }

    public List<List> check(List<String> idList, String bizdate, String username) {
        List<List> ret = new ArrayList<>();
        try {
            for (String id : idList) {
                List result = (List) jdbcTemplateMysql.execute(
                        con -> {
                            String storedProc = "{CALL glsc_data.p_reg_rpt_check" + "(?,?,?,?,?)}";// 调用的sql
                            CallableStatement cs = con.prepareCall(storedProc);
                            cs.setString(1, id);
                            cs.setString(2, bizdate);
                            cs.setString(3, username);
                            cs.registerOutParameter(4, 4);//设置输出参数 —— 必须在执行存储过程之前进行注册
                            cs.registerOutParameter(5, 12);
                            return cs;
                        }, (CallableStatementCallback) cs -> {
                            List<String> res = new ArrayList<>();
                            cs.execute();
                            Integer a = cs.getInt(4);
                            String s = String.valueOf(a);
                            res.add(s);
                            String ss = cs.getString(5);
                            res.add(ss);
                            return res;
                        });
                ret.add(result);
            }
        } catch (Exception e) {
            throw DerivativeException.error(e.getMessage());
        }

        return ret;
    }

    public List<JSONObject> getCheckResult(BigInteger id, String bizdate) {
        return regRptCheckRulesResultRepo.getResultDtoList(id, bizdate);
    }

    public List<RegRptCheckRulesDefine> getCheckRuleList() {
        return regRptCheckRulesDefineRepo.findAllExist();
    }

    public List<RegRptCheckRulesDefine> getCheckRuleListByCatId(String catId) {
        return regRptCheckRulesDefineRepo.findListByCatId(catId);
    }

    /**
     * @author: qiyr
     * @date: 2022/7/18 9:47
     * @description: 报表导出，默认导出格式为xlsx
     */
    public List<BiExportDto> export(List<RegRptInfoDto> dtoList) {
        List<BiExportDto> biExportDtoList = new ArrayList<>();
        Map<BigInteger, RegRptDefine> defineMap = regRptDefineRepo.findAll().stream().collect(Collectors.toMap(RegRptDefine::getId, item -> item));
        // 导出报表
        for (RegRptInfoDto dto : dtoList) {
            RegRptDefine regRptDefine = defineMap.get(dto.getId());
            Map<String, String> biParamReportDate = Map.of(BI_PARAM_REPORT_DATE, dto.getBizdate(), BI_PARAM_DIMENSION1, dto.getDimension1());
            BiExportDto biExportDto = biUserManager.doExport(regRptDefine.getBiReportId(),
                    ReportExportFileTypeEnum.XLSX.getType(), MessageFormat.format(BI_REPORT_NAME,
                            regRptDefine.getReportName(), dto.getBizdate(), dto.getDimension1()), biParamReportDate, regRptDefine.getBiVersion());
            biExportDtoList.add(biExportDto);
        }
        return biExportDtoList;
    }

    public RegRptDefine getRptDefineById(BigInteger id) {
        return regRptDefineRepo.getById(id);
    }

    public List<RegRptDefine> findAllRptDefineExist() {
        return regRptDefineRepo.findAllExist();
    }

    public List<RegRptDefine> getMergedList(ExcelUploadDto dto) {
        JSONArray jsonArray = dto.getData();
        List<BigInteger> importIds = new ArrayList<>();
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            importIds.add(jsonObject.getBigInteger("importid"));
        }
        return regRptDefineRepo.findMergedList(importIds, dto.getBizdate());
    }

    /**
     * @author: qiyr
     * @date: 2023/3/13 15:47
     * @description: 对监管报送定义额外过滤处理
     */
    private void defineFilter(List<RegRptDefine> importConfigList, String bizdate) {
        Iterator<RegRptDefine> iterator = importConfigList.iterator();
        while (iterator.hasNext()) {
            RegRptDefine define = iterator.next();
            // 过滤无效定义数据
            if (NumberUtils.INTEGER_ZERO.equals(define.getIsEnable())) {
                iterator.remove();
                continue;
            }
            // 过滤未启用定义数据
            Date bizDate = null;
            try {
                bizDate = new SimpleDateFormat("yyyyMM").parse(bizdate);
            } catch (ParseException parseException) {
                logger.error("业务日期解析异常");
                return;
            }
            if (define.getEnableDate() != null && bizDate.before(define.getEnableDate())) {
                iterator.remove();
                continue;
            }
            // 过滤已过期定义数据
            if (define.getExpiryDate() != null && !bizDate.before(define.getExpiryDate())) {
                iterator.remove();
                continue;
            }
        }
    }
}
